package com.tianji.promotion.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.constants.PromotionConstants;
import com.tianji.promotion.domain.dto.CouponFormDTO;
import com.tianji.promotion.domain.dto.CouponIssueFormDTO;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.CouponScope;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.CouponQuery;
import com.tianji.promotion.domain.vo.CouponDetailVO;
import com.tianji.promotion.domain.vo.CouponPageVO;
import com.tianji.promotion.domain.vo.CouponScopeVO;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.enums.CouponStatus;
import com.tianji.promotion.enums.ObtainType;
import com.tianji.promotion.enums.UserCouponStatus;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.service.ICouponScopeService;
import com.tianji.promotion.service.ICouponService;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.tianji.promotion.enums.CouponStatus.*;

/**
 * <p>
 * 优惠券的规则信息 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-04-18
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {
    private final ICouponScopeService scopeService;
    private final IExchangeCodeService exchangeCodeService;
    private final CourseClient courseClient;
    private  final CatalogueClient catalogueClient;
    private final IUserCouponService userCouponService;
    private final StringRedisTemplate redisTemplate;
    @Override
    @Transactional
    public void addCoupon(CouponFormDTO dto) {
        log.info("新增优惠券，优惠券信息为：{}", dto);
        log.info("新增优惠券，优惠券类型信息为：{}", dto.getScopes().toString());
        Coupon coupon = BeanUtil.copyProperties(dto, Coupon.class);
        coupon.setType(1);
        coupon.setCreater(UserContext.getUser());
        coupon.setUpdater(UserContext.getUser());
        this.save(coupon);
        if (!dto.getSpecific()) {
            return;
        }
        Long couponId = coupon.getId();
        ArrayList<CouponScope> list = new ArrayList<>(dto.getScopes().size());
        for (Long scope : dto.getScopes()) {
            CouponScope couponScope = new CouponScope();
            couponScope.setCouponId(couponId);
            couponScope.setBizId(scope);
           /* if (scope>1000){
                couponScope.setType(1);
            }else {
                couponScope.setType(2);
            }*/
            couponScope.setType(1);
            list.add(couponScope);
        }
        scopeService.saveBatch(list);
       /* // 1.保存优惠券
        // 1.1.转PO
        Coupon coupon = BeanUtils.copyBean(dto, Coupon.class);
        // 1.2.保存
        save(coupon);

        if (!dto.getSpecific()) {
            // 没有范围限定
            return;
        }
        Long couponId = coupon.getId();
        // 2.保存限定范围
        List<Long> scopes = dto.getScopes();
        if (CollUtils.isEmpty(scopes)) {
            throw new BadRequestException("限定范围不能为空");
        }
        // 2.1.转换PO
        List<CouponScope> list = scopes.stream()
                .map(bizId -> new CouponScope().setBizId(bizId).setCouponId(couponId))
                .collect(Collectors.toList());
        // 2.2.保存
        scopeService.saveBatch(list);*/
    }




    @Override
    public PageDTO<CouponPageVO> queryCouponPage(CouponQuery query) {
        Integer type = query.getType();
        String name = query.getName();
        Integer status = query.getStatus();
        Page<Coupon> page = this.lambdaQuery()
                .eq(type != null, Coupon::getType, type)
                .eq(status != null, Coupon::getStatus, status)
                .like(StringUtils.isNotBlank(name), Coupon::getName, name)
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        List<Coupon> records = page.getRecords();
        if (CollUtils.isEmpty(records)){
            return PageDTO.empty(page);
        }
        ArrayList<CouponPageVO> list = new ArrayList<>(records.size());
        for (Coupon record : records) {
            list.add(BeanUtil.copyProperties(record, CouponPageVO.class));
        }
        return PageDTO.of(page, list);
    }

    @Override
    public void issueCoupon(Long id, CouponIssueFormDTO dto) {
        Coupon coupon = getById(id);
        if (coupon == null){
            throw new BizIllegalException("优惠卷不存在");
        }
        if (coupon.getStatus() != CouponStatus.DRAFT && coupon.getStatus() != PAUSE){
            throw new BizIllegalException("优惠券状态错误！");
        }
        log.info("修改优惠券，优惠券信息为：{}", dto.getIssueBeginTime());
        /*LocalDateTime issueBeginTime = dto.getIssueBeginTime();
        this.lambdaUpdate()
                .set(dto.getIssueBeginTime()!=null, Coupon::getIssueBeginTime, dto.getIssueBeginTime())//发放开始时间不为空，为指定时间发放
                .set(dto.getIssueBeginTime()==null, Coupon::getIssueBeginTime, LocalDateTime.now())//发放开始时间为空，为立刻发放
                .set(Coupon::getIssueEndTime, dto.getIssueEndTime())
                .set(dto.getTermDays()!=null, Coupon::getTermDays, dto.getTermDays())
                .set(dto.getTermBeginTime()!=null,Coupon::getTermBeginTime, dto.getTermBeginTime())
                .set(dto.getTermEndTime()!=null,Coupon::getTermEndTime, dto.getTermEndTime())
                .set(issueBeginTime == null || !issueBeginTime.isAfter(LocalDateTime.now()),Coupon::getStatus, ISSUING)
                .eq(Coupon::getId, id).update();*/
        // 3.判断是否是立刻发放
        LocalDateTime issueBeginTime = dto.getIssueBeginTime();
        LocalDateTime now = LocalDateTime.now();
        boolean isBegin = issueBeginTime == null || !issueBeginTime.isAfter(now);
        // 4.更新优惠券
        // 4.1.拷贝属性到PO
        Coupon c = BeanUtils.copyBean(dto, Coupon.class);
        // 4.2.更新状态
        if (isBegin) {
            c.setStatus(ISSUING);
            c.setIssueBeginTime(now);
        }else{
            c.setStatus(UN_ISSUE);
        }
        // 4.3.写入数据库
        updateById(c);
        // 5.写入redis
        if (isBegin){
            coupon.setIssueBeginTime(c.getIssueBeginTime());
            coupon.setIssueEndTime(c.getIssueEndTime());
            cacheCouponInfo(coupon);
        }

        //  兑换码生成 异步生成
        //1.查询该优惠卷的状态条件，状态：待发放    获取方式：2.兑换码
        if (coupon.getObtainWay() == ObtainType.ISSUE && coupon.getStatus() == CouponStatus.DRAFT ){
            coupon.setIssueEndTime(c.getIssueEndTime());
            exchangeCodeService.asyncGenerateCode(coupon);
        }
    }

    private void cacheCouponInfo(Coupon coupon) {
        //1.拼接key 前缀＋Id
        String key = PromotionConstants.COUPON_CACHE_KEY_PREFIX + coupon.getId();
        HashMap<String, String> map = new HashMap<>();
        //格式化时间为yyyMMdd
        map.put("issueBeginTime", DateUtil.format(coupon.getIssueBeginTime(), "yyyyMMdd"));
        map.put("issueEndTime", DateUtil.format(coupon.getIssueEndTime(), "yyyyMMdd"));
        map.put("totalNum", String.valueOf(coupon.getTotalNum()));
        map.put("userLimit", String.valueOf(coupon.getUserLimit()));
        log.info("发放优惠券，将优惠卷信息保存到redis中：{}",map);
        redisTemplate.opsForHash().putAll(key, map);
    }

    @Override
    public void updateCoupon(Long id, CouponFormDTO dto) {
        Coupon coupon = BeanUtil.copyProperties(dto, Coupon.class);
        coupon.setId(id);
        coupon.setUpdater(UserContext.getUser());
        this.updateById(coupon);
    }

    @Override
    public CouponDetailVO queryById(Long id) {
        //1.根据id查询db
        Coupon coupon = getById(id);
        //2.封装vo
        CouponDetailVO vo = BeanUtil.copyProperties(coupon, CouponDetailVO.class);
        //2.1根据优惠卷查询 优惠券作用范围的业务id表 得到bizid
        CouponScope couponScope = scopeService.lambdaQuery().eq(CouponScope::getCouponId, id).one();
        CouponScopeVO scopeVO = new CouponScopeVO();
        if (couponScope == null){
            vo.setScopes(new ArrayList<>());
        } else if (couponScope.getType()==1){
            //2.2.如果是分类，则查询分类名称
            List<CataSimpleInfoDTO> dtos = catalogueClient.batchQueryCatalogue(Collections.singletonList(couponScope.getBizId()));
            if (CollUtils.isEmpty(dtos)){
                vo.setScopes(new ArrayList<>());
            }else {
                scopeVO.setId(couponScope.getBizId());
                scopeVO.setName(dtos.get(0).getName());
                vo.setScopes(Collections.singletonList(scopeVO));
            }
        }else{
            //2.3.如果是课程，则查询课程名称
            List<CourseSimpleInfoDTO> courseInfo = courseClient.getSimpleInfoList(Collections.singletonList(couponScope.getBizId()));
            if (CollUtils.isEmpty(courseInfo)){
                vo.setScopes(new ArrayList<>());
            }else {
                scopeVO.setId(couponScope.getBizId());
                scopeVO.setName(courseInfo.get(0).getName());
                vo.setScopes(Collections.singletonList(scopeVO));
            }
        }
        return vo;
    }

    @Override
    public void pauseCoupon(Long id) {
        lambdaUpdate()
                .set(Coupon::getStatus, CouponStatus.PAUSE)
                .eq(Coupon::getUpdater, UserContext.getUser())
                .set(Coupon::getUpdateTime, LocalDateTime.now())
                .eq(Coupon::getId, id)
                .update();
        //删除redis中的数据
        redisTemplate.delete(PromotionConstants.COUPON_CACHE_KEY_PREFIX + id);
       /* Coupon coupon = getById(id);
        coupon.setStatus(CouponStatus.PAUSE);
        updateById(coupon);*/
    }

    @Override
    /**
     * 是否可以领取：优惠券还有剩余并且用户已领取数量未超过限领数量
     * 是否已领取：用户是否有已经领取，尚未使用的券
    * */
    public List<CouponVO> queryCouponList() {
        // 1.查询优惠券列表
        List<Coupon> coupons = lambdaQuery()
                .eq(Coupon::getStatus, CouponStatus.ISSUING)
                .eq(Coupon::getObtainWay, ObtainType.PUBLIC)
                .list();
        if (CollUtils.isEmpty(coupons)){
            return Collections.emptyList();
        }
       //2.查询用户优惠卷信息
        //2.1通过stream流获取优惠卷集合
        List<Long> couponIds = coupons.stream().map(Coupon::getId).collect(Collectors.toList());
        List<UserCoupon> userInfo=userCouponService.queryByCouponId(UserContext.getUser(),couponIds);
        //2.2转为map<CouponId,count>
        Map<Long, Long> UserMap = userInfo.stream().collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));
        Map<Long, Long> UNUSED_MAP = userInfo.stream()
                .filter(uc -> uc.getStatus() == UserCouponStatus.UNUSED)
                .collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));
        ArrayList<CouponVO> list = new ArrayList<>(coupons.size());
        for (Coupon coupon : coupons) {
            CouponVO vo = BeanUtil.copyProperties(coupon, CouponVO.class);
            boolean isAvailable = coupon.getTotalNum() > coupon.getIssueNum();
           if (UserMap.get(coupon.getId()) != null){
               isAvailable = isAvailable && UserMap.get(coupon.getId()) < coupon.getUserLimit();
           }
            vo.setAvailable(isAvailable);//是否可以领取
            boolean isReceived = false;
           if (UNUSED_MAP.get(coupon.getId()) != null){
               isReceived= UNUSED_MAP.get(coupon.getId()) > 0;
           }
            vo.setReceived(isReceived);//是否已领取
            list.add(vo);
        }
        return list;
    }
}
